﻿using System;
using System.Globalization;
using QAToolsAddin.Comparer;
using QAToolsAddin.Components;
using QAToolsAddin.GenericDialogs;
using Excel = Microsoft.Office.Interop.Excel;

namespace QAToolsAddin.CustomDialogs {
    public partial class FrmDistinct : ExcelForm {
        private readonly Excel.Application _excelapp;
        private Excel.Workbook _workbook;
        private Excel.Worksheet _worksheet;
        private readonly object Missing = Type.Missing;

        public FrmDistinct(Excel.Application excelapp, CultureInfo culture)
            : base(excelapp) {
            _excelapp = excelapp;
            InitializeComponent();
        }

        private void LoadSettings() {
            ctrlOptCells.Checked = (bool)Settings.GetParam(this, "ctrlOptCells", true);
            ctrlOptRows.Checked = (bool)Settings.GetParam(this, "ctrlOptRows", false);
            ctrlIgnoreCase.Checked = (bool)Settings.GetParam(this, "ctrlIgnoreCase", false);
            ctrlUseFormula.Checked = (bool)Settings.GetParam(this, "ctrlUseFormula", false);
            ctrlInteriorColor.SelectedColor = (int)Settings.GetParam(this, "ctrlInteriorColor", 0xFF);
            ctrlFontColor.SelectedColor = (int)Settings.GetParam(this, "ctrlFontColor", 0xFFFFFF);
            ctrlOptCurrentSel.Checked = (bool)Settings.GetParam(this, "ctrlOptCurrentSel", false);
            ctrlOptNewSheet.Checked = (bool)Settings.GetParam(this, "ctrlOptNewSheet", true);
            ctrlOptHighligth.Checked = (bool)Settings.GetParam(this, "ctrlOptHighligth", true);
            ctrlOptDelete.Checked = (bool)Settings.GetParam(this, "ctrlOptDelete", true);
        }

        private void SaveSettings() {
            Settings.SetParam(this, "ctrlOptCells", ctrlOptCells.Checked);
            Settings.SetParam(this, "ctrlOptRows", ctrlOptRows.Checked);
            Settings.SetParam(this, "ctrlIgnoreCase", ctrlIgnoreCase.Checked);
            Settings.SetParam(this, "ctrlUseFormula", ctrlUseFormula.Checked);
            Settings.SetParam(this, "ctrlInteriorColor", ctrlInteriorColor.SelectedColor);
            Settings.SetParam(this, "ctrlFontColor", ctrlFontColor.SelectedColor);
            Settings.SetParam(this, "ctrlOptCurrentSel", ctrlOptCurrentSel.Checked);
            Settings.SetParam(this, "ctrlOptNewSheet", ctrlOptNewSheet.Checked);
            Settings.SetParam(this, "ctrlOptHighligth", ctrlOptHighligth.Checked);
            Settings.SetParam(this, "ctrlOptDelete", ctrlOptDelete.Checked);
        }

        private void ApplyFieldsRules() {
            SuspendLayout();

            bool optionrow = ctrlOptRows.Checked;
            lblKeys.Enabled = optionrow;
            ctrlKeys.Enabled = optionrow;
            ctrlWithHeaders.Enabled = optionrow;

            bool highlight = ctrlOptHighligth.Checked;
            lblInteriorColor.Enabled = highlight;
            lblFontColor.Enabled = highlight;
            ctrlInteriorColor.Enabled = highlight;
            ctrlFontColor.Enabled = highlight;

            ResumeLayout();
        }

        private void FrmDistinct_Load(object sender, EventArgs e) {
            _workbook = _excelapp.ActiveWorkbook;
            _worksheet = (Excel.Worksheet)_excelapp.ActiveSheet;
            if (_workbook == null || _worksheet == null)
                throw new InputError("Open a workbook first!");

            LoadSettings();
            ApplyFieldsRules();
            Excel.Range selection = _excelapp.ActiveWindow.RangeSelection;

            if (_excelapp.ActiveWindow.SelectedSheets.Count != 1)
                throw new InputError("Multi-Sheet selection is not allowed!");

            if (selection.Worksheet.UsedRange.Cells.Count != 1) {
                Excel.Range sel1 = Utils.ExtendRange(selection.Areas[1]);
                ctrlWithHeaders.Checked = Utils.RangeHasHeaders(sel1);
                if (sel1 != null)
                    ctrlRef.Range = sel1;
            }
        }

        private void UpdateKeys_Delegate(object sender, EventArgs e) {
            if (ctrlOptRows.Checked)
                ctrlKeys.SetColumnSelector(ctrlRef.Range, ctrlWithHeaders.Checked);
        }

        private void btDistinct_Click(object sender, EventArgs e) {
            try {
                Utils.SwitchExcelToIdle(_excelapp, false);
                RunDistinct();
            } catch (InputError ex) {
                MsgBox.ShowError(_excelapp, ex.Message);
            } finally {
                Utils.SwitchExcelToIdle(_excelapp, true);
            }
        }

        private void RunDistinct() {
            if (ctrlRef.IsEmpty) throw new InputError("Selection is empty!");

            //Shrink to used range
            Excel.Range rgSel = _excelapp.Intersect(ctrlRef.Range, ctrlRef.Worksheet.UsedRange);
            if (rgSel == null) throw new InputError("Selection is empty!");
            if (rgSel.Rows.Count == 1) throw new InputError("Selection must contain at least 2 rows!");

            //Define data range
            bool optionformula = ctrlUseFormula.Checked;
            int nbRow = rgSel.Rows.Count, nbCol = rgSel.Columns.Count;
            int offset = ctrlOptRows.Checked && ctrlWithHeaders.Checked ? 1 : 0;
            Excel.Range rgData = rgSel.Offset[offset, Type.Missing].Resize[nbRow - offset, Type.Missing];
            if (rgData.Cells.Count < 2) throw new InputError("Range 1 must contain at least 2 cells!");

            //read data
            object[,] data = Utils.GetDataFromRange(rgData, optionformula);
            object[] formats = Utils.GetColumnsFormating(rgData);

            //Initilise caompare class and save seeting
            var compare = new Compare { IgnoreCase = ctrlIgnoreCase.Checked };
            SaveSettings();

            //Compare
            CellSet result = null;
            if (ctrlOptRows.Checked) {
                int[] keysCol = ctrlKeys.IsEmpty ? ctrlKeys.UncheckedIds : ctrlKeys.CheckedIds;
                int[] valCol = Utils.GetVisibleColumnsPosition(rgData, null, keysCol);
                if (ctrlOptDelete.Checked)
                    result = compare.DeleteDuplicatedRows(ref data, keysCol, valCol);
                else if (ctrlOptHighligth.Checked)
                    result = compare.GetDuplicatedRows(ref data, keysCol, valCol);
            } else {
                if (ctrlOptDelete.Checked)
                    result = compare.DeleteDuplicatedCells(ref data);
                else if (ctrlOptHighligth.Checked)
                    result = compare.GetDuplicatedCells(ref data);
            }

            //Add new sheet, formats and headers
            if (ctrlOptNewSheet.Checked) {
                var worksheet = (Excel.Worksheet)_workbook.Sheets.Add(Missing, _workbook.Sheets[_workbook.Sheets.Count], Type.Missing, Type.Missing);
                var headers = offset != 0 ? rgSel.Resize[1, Type.Missing].Value : null;
                rgData = ((Excel.Range)worksheet.Cells[1 + offset, 1]).Resize[nbRow - offset, nbCol];
                Utils.SetColumnsFormat(rgData, nbRow - offset, formats, 1, 1);
                if (headers != null) {
                    var rgHeader = ((Excel.Range)rgData.Rows[1]).Offset[offset, Type.Missing];
                    rgHeader.Value = headers;
                    rgHeader.Font.Bold = true;
                }
                ((Excel._Worksheet)worksheet).Activate();
            }

            //Add data and diff tags
            if (result == null) return;
            if (ctrlOptDelete.Checked || ctrlOptNewSheet.Checked)
                Utils.AddDataToRange(rgData, result.Data, optionformula);
            Utils.AddTagsToRange(rgData, result.Diff, ctrlInteriorColor.SelectedColor, ctrlFontColor.SelectedColor);

        }

        private void btClose_Click(object sender, EventArgs e) {
            Close();
        }

        private void ctrlOptType_CheckedChanged(object sender, EventArgs e) {
            ApplyFieldsRules();
        }

        private void ctrlOptAction_CheckedChanged(object sender, EventArgs e) {
            ApplyFieldsRules();
        }

        private void ctrlRef_Escape(object sender, EventArgs e) {
            btClose.Focus();
        }

    }
}
